#ifndef __My_SERVER__
#define __My_SERVER__

#include "data.hpp"
#include "httplib.h"

extern cloud::DataManager *_data;

namespace cloud
{
    class server
    {
    public:
        server()
        {
            // 使用配置文件进行初始化处理
            Config *config = Config::GetInstance();

            _server_port = config->GetServerPort();
            _server_ip = config->GetServerIP();
            _download_prefix = config->GetDownloadPrefix();
        }

        // 业务处理函数
        bool RunModule()
        {
            // 文件上传请求
            _server.Post("/upload", Upload);

            // 文件总页面展示请求
            _server.Get("/listshow", ListShow);
            _server.Get("/", ListShow); // 默认请求

            // 文件下载请求
            std::string download_url = _download_prefix + "(.*)";
            //(.*)是正则表达式；. 点表示任意字符，*表示前面的字符可以出现任意次数，一次或者多次
            _server.Get(download_url.c_str(), Download);

            // 捕获客户端连接
            _server.listen(_server_ip.c_str(), _server_port);

            return true;
        }

    private:
        // 文件上传请求处理函数
        static void Upload(const httplib::Request &req, httplib::Response &rsp)
        {
            // 整个请求报文的正文不全为文件数据
            auto ret = req.has_file("file");
            if (ret == false) // 判断有没有上传文件区域
            {
                rsp.status = 400;
                return;
            }

            const auto &file = req.get_file_value("file"); // 获取请求中的文件数据
            // file.filename文件名   file.content文件内容

            // 备份文件所在的目录
            std::string back_dir = Config::GetInstance()->GetBackDir();
            // 文件路径 == 备份文件所在的目录+文件名
            std::string realpath = back_dir + FileUtil(file.filename).FileName();

            FileUtil fu(realpath);
            // 将上传文件的内容写入到realpath中
            fu.SetContent(file.content);

            // 数据备份好，将数据管理起来
            BackupInfo info;
            info.NewBackupInfo(realpath); // 组织备份文件的信息
            _data->Insert(info);          // 向数据管理模块插入新的备份文件信息
        }

        // 时间戳转特定的时间格式
        static std::string TimeToStr(const time_t t)
        {
            std::string tmp = std::ctime(&t);
            return tmp;
        }

        // 文件总页面展示请求处理函数
        static void ListShow(const httplib::Request &req, httplib::Response &rsp)
        {
            // 1.获取所有的备份文件信息
            std::vector<BackupInfo> array;
            _data->GetAll(&array);

            // 2.根据备份文件的信息，组织html文件格式
            std::stringstream ss;

            ss << "<!DOCTYPE html><html lang='en'><head><meta charset='UTF-8'>";
            ss << "<meta name='viewport' content='width=device-width, initial-scale=1.0'>";
            ss << "<title>文件下载页面</title><style>";
            ss << "body {font-family: Arial, sans-serif;margin: 40px;}";
            ss << ".download-table{width: 100%;border-collapse: collapse; margin-top: 20px;}";
            ss << ".download-table th, .download-table td {border: 1px solid #ddd;padding: 8px;text-align: left;}";
            ss << ".download-table th { background-color: #f2f2f2;}";
            ss << ".download-table .date, .download-table .size {text-align: right;}";
            ss << ".download-link {text-decoration: none;color: #007bff; }";
            ss << ".download-link:hover { text-decoration: underline; }";
            ss << " </style> </head><body><h1>文件下载</h1>";
            ss << "<p>请从下面的列表中选择要下载的文件。</p> <table class='download-table'>";
            ss << "<thead><tr><th>文件名</th><th class='date'>日期</th> <th class='size'>大小</th></tr></thead><tbody>";

            for (auto &a : array)
            {
                ss << "<tr><td>";
                std::string filename = FileUtil(a.real_path).FileName();
                ss << "<a href='" << a.url << "'class='download-link'>" << filename << "</a>";
                ss << "</td><td class='date'>" << TimeToStr(a.mtime) << "</td>"; // 显示最后修改时间

                std::string filesize = std::to_string(a.fsize / 1024);
                filesize += ".";
                filesize += std::to_string(a.fsize % 1024);

                ss << "<td class='size'>" << filesize << "k</td></tr>";    // 文件大小以k显示
            }
            ss << "</tbody></table></body></html>";

            // 将刚刚编好的html格式回应给客户端
            rsp.body = ss.str();
            // 设置头部内容为html文本
            rsp.set_header("Content-Type", "text/html");
            rsp.status = 200;
        }

        // 获取唯一标识符：文件名+文件大小+文件最后一次修改时间
        static std::string GetETag(const BackupInfo &info)
        {
            FileUtil fu(info.real_path);
            std::string etag = fu.FileName();
            etag += '-';
            etag += std::to_string(info.fsize);
            etag += '-';
            etag += std::to_string(info.mtime); // 最后一次修改时间

            return etag;
        }

        // 文件下载请求处理函数
        static void Download(const httplib::Request &req, httplib::Response &rsp)
        {
            // 1.获取客户端请求的资源路径path   req.path
            // 2.根据资源路径，获取文件备份信息
            BackupInfo info;
            _data->GetOneByUrl(req.path, &info); // 通过资源路径获取备份文件的信息

            // 3.文件是否被压缩？是，先进行解压；删除对应的压缩包，修改文件数据信息
            if (info.pack_flag == true)
            {
                FileUtil fu(info.pack_path); // 压缩文件所在位置
                // 将文件解压到备份文件路径下
                fu.UnCompress(info.real_path);
                fu.Remove();
                // 更新数据信息
                info.pack_flag = false;
                _data->UpDate(info);
            }
            // 4.读取文件信息，将内容设置到rsp中
            FileUtil fu(info.real_path); // 备份文件
            fu.GetContent(&rsp.body);

            bool retrans = false; // 用于标识是否符合断点续传
            std::string old_etag;
            if (req.has_header("If-Range"))
            {
                old_etag = req.get_header_value("If-Range");
                // 含有If-Range字段，If-Range字段的etag值与请求的文件最新的etag一致，则符合断点续传
                if (old_etag == GetETag(info))
                    retrans = true;
            }

            // 5.设置响应报头字段：ETag、Accept-Ranges:bytes;
            if (retrans == false) 
            {
                // 不符合断点续传，直接传整个备份文件
                fu.GetContent(&rsp.body);

                rsp.set_header("Accept-Ranges", "bytes");                   // 通过特定的区间来访问资源
                rsp.set_header("ETag", GetETag(info));                      // 用于确定文件是否被修改
                rsp.set_header("Content-Type", "application/octet-stream"); // 响应的内容被视为任意的二进制数据
                rsp.status = 200;
            }
            else // 符合断点续传
            {
                //httplib库内部实现了断点续传的功能
                //在这里只需要将文件的内容读取到body，httplib内部函数会自动处理对应区间数据
                //从body中取出指定区间数据进行响应

                fu.GetContent(&rsp.body);
                rsp.set_header("Accept-Ranges", "bytes"); // 通过特定的区间来访问资源
                rsp.set_header("ETag", GetETag(info));
                rsp.status = 206;
            }
        }

    private:
        int16_t _server_port;
        std::string _server_ip;
        std::string _download_prefix; // 文件下载资源路径
        httplib::Server _server;
    };
}

#endif